/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2017年1月30日
 * V4.0
 */
package com.jphenix.kernel.classloader;

import com.jphenix.kernel.objectloader.util.JarResourceVO;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SListMap;
import com.jphenix.share.lang.SortVector;
import com.jphenix.share.tools.IniConfProp;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.docs.Running;

import java.io.File;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * 内置资源加载器
 * 
 * 内置资源必须放在压缩包的  resources 文件夹中，并且
 *resources文件夹中需要有 标识文件 .jpxres
 *
 *.jpxres 的内容格式为ini配置文件格式（注释可以用#符号，但只能单独一行）
 *
 * 配置文件中必须包含 ver 的值，格式位 yyyy-mm-dd hh:mm  的时间格式（没有秒信息）
 * 比如： ver = 2017-02-17 17:56
 *
 *程序加载时，先加载以前时间的资源包，如果加载时遇到同名同路径
 *的文件，则将覆盖以前的文件
 *
 * 2018-09-05 这是内核调用的类，在调用时还没做初始化，所以去掉基类ABase父类
 * 
 * @author MBG
 * 2017年1月30日
 */
@ClassInfo({"2018-09-05 15:47","内置资源加载器"})
@BeanInfo({"resourcesloader"})
@Running({"99","init","","release"})
public class ResourcesLoader {

	//由于 压缩包中不保存单独的目录信息，
	//需要从文件中提取出目录信息，判断压缩包中是否存在该目录
	private List<String> pathList = null;
	private SListMap<JarResourceVO> entriesMap = null; //文件元素容器
	private List<ZipFile> jarList = null; //符合条件的压缩包序列
	//资源包中的配置信息对照容器   按照时间先后顺序覆盖同名参数
	private Map<String,Map<String,String>> resPropertyMap = new HashMap<String,Map<String,String>>();

	/**
	 * 构造函数
	 * @author MBG
	 */
	public ResourcesLoader() {
		super();
	}
	
	/**
	 * 获取指定段的参数对照容器
	 * @param title 参数段主键
	 * @return 参数对照容器
	 * 2017年2月17日
	 * @author MBG
	 */
	public Map<String,String> getPropertyMap(String title){
		if(title==null) {
			title = "";
		}
		//获取返回值
		Map<String,String> reMap = resPropertyMap.get(title);
		if(reMap==null) {
			return new HashMap<String,String>();
		}
		return reMap;
	}
	
	/**
	 * 是否存在指定文件
	 * @param subPath 文件相对路径
	 * @return 是否存在指定文件
	 * 2017年2月1日
	 * @author MBG
	 */
	public boolean hasFile(String subPath) {
		return entriesMap.containsKey(subPath);
	}
	
	/**
	 * 压缩包中是否存在该路径
	 * @param subPath 待判断路径
	 * @return 是否存在该路径
	 * 2017年2月1日
	 * @author MBG
	 */
	public boolean hasPath(String subPath) {
		return pathList.contains(subPath);
	}
	
	/**
	 * 获取文件最后更新时间
	 * @param subPath 文件相对路径
	 * @return 文件最后更新时间
	 * 2017年2月1日
	 * @author MBG
	 */
	public long getFileTime(String subPath) {
		//获取内部文件
		JarResourceVO jVO = entriesMap.get(subPath);
		if(jVO==null) {
			return 0;
		}
		return jVO.entrie.getTime();
	}
	
	/**
	 * 获取压缩文件元素读入流
	 * @param subPath 压缩文件相对路径
	 * @return 对应的文件读入流
	 * @throws Exception 异常
	 * 2017年2月1日
	 * @author MBG
	 */
	public InputStream getFile(String subPath) throws Exception {
		//获取内部文件
		JarResourceVO jVO = entriesMap.get(subPath);
		if(jVO==null) {
			throw new Exception("Not Find The Jar File From Path:["+subPath+"]");
		}
		return jVO.jarFile.getInputStream(jVO.entrie);
	}
	
	
	/**
	 * 获取对应的压缩资源信息容器
	 * @param subPath 相对路径
	 * @return 压缩资源信息容器
	 * @throws Exception 异常
	 * 2017年2月22日
	 * @author MBG
	 */
	public JarResourceVO getResourceVO(String subPath) throws Exception {
		//获取内部文件
		return entriesMap.get(subPath);
	}
	

	
	/**
	 * 返回与路径开头匹配的自路径序列
	 * @param baseSubPath 开头路径
	 * @return 匹配路径序列
	 * 2017年1月31日
	 * @author MBG
	 */
	public List<String> getSubPathList(String baseSubPath){
		if(!baseSubPath.startsWith("/")) {
			baseSubPath = "/"+baseSubPath;
		}
		//构建返回值
		List<String> reList = new ArrayList<String>();
		for(String path:entriesMap.keys()) {
			if(path.startsWith(baseSubPath)) {
				reList.add(path);
			}
		}
		return reList;
	}

	
	/**
	 * 加载内置资源信息
	 * @param basePath 网站根路径 WEB-INF 的路径
	 * 2017年1月30日
	 * @author MBG
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public void load(String basePath) {
		if(basePath==null || basePath.length()<1) {
			return;
		}
		jarList    = new ArrayList<ZipFile>();
		entriesMap = new SListMap<JarResourceVO>();
		pathList   = new ArrayList<String>();
		
		System.out.println("\n\nBegin Load JPhenix Native Resources...");
		
		//搜索jar包并检查是否存在文件 /resources/JPHENIX.RES 文件
		//并按照其内部的时间按照顺序排列加载资源文件，后加载的重复文件
		//覆盖先加载的文件
		SortVector[] svs = searchJar(basePath+"/lib");
		
		//压缩包内容列表迭代
		Enumeration<? extends ZipEntry> entries;
		ZipEntry ele;   //元素 
		String subPath; //相对路径
		String path;    //文件路径 
		SortVector<ZipFile> jarSv = (SortVector<ZipFile>)svs[0];
		ZipFile jarEle; //压缩包元素
		while(jarSv.hasNext()) {
			jarEle = jarSv.nextValue();
			entries = jarEle.entries();
			jarList.add(jarEle);
			while(entries.hasMoreElements()) {
				ele = entries.nextElement();
				subPath = ele.getName();
				if(!subPath.startsWith("resources")) {
					continue;
				}
				subPath = subPath.substring(9);
				if("/.jpxres".equals(subPath)) {
					continue;
				}
				entriesMap.put(subPath,new JarResourceVO(jarEle,ele));
				
				path = SFilesUtil.getFilePath(subPath);
				if(!pathList.contains(path)) {
					pathList.add(path);
				}
			}
		}
		//配置信息序列容器
		SortVector<IniConfProp>        icpSv = (SortVector<IniConfProp>)svs[1];
		Map<String,Map<String,String>> propMap; //配置文件对照容器
		List<String>                   keyList; //段主键序列
		Map<String,String>             subMap;  //段参数容器 
		Map<String,String>             lMap;    //已经保存好的段参数容器
		while(icpSv.hasNext()) {
			propMap = icpSv.nextValue().valueMap();
			propMap.remove("ver");
			keyList = BaseUtil.getMapKeyList(propMap);
			for(String key:keyList) {
				subMap = propMap.get(key);
				if(subMap==null) {
					continue;
				}
				if(resPropertyMap.containsKey(key)) {
					lMap = resPropertyMap.get(key);
					if(lMap==null) {
						resPropertyMap.put(key,subMap);
					}else {
						lMap.putAll(subMap);
					}
				}else {
					resPropertyMap.put(key,subMap);
				}
			}
		}
		System.out.println("Load JPhenix Native Resources Completed. Total Jar:["
		+jarList.size()+"] Total Entries:["+entriesMap.size()+"]");
	}
	
	/**
	 * 释放资源
	 * 2017年1月31日
	 * @author MBG
	 */
	public void release() {
		if(jarList!=null) {
			for(ZipFile ele:jarList) {
				try {
					ele.close();
				}catch(Exception e) {}
				ele = null;
			}
		}
		jarList = null;
	}

	
	/**
	 * 搜索jar包并检查是否存在文件 /resources/JPHENIX.RES 文件
	 * 并按照其内部的时间按照顺序排列加载资源文件，后加载的重复文件
	 * 覆盖先加载的文件
	 * @param jarBasePath jar包类的根路径
	 * @return 0 符合条件的包序列（按照内部JPHENIX.RES文件中的时间顺序排列） 1资源包的配置信息
	 * 2017年1月30日
	 * @author MBG
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private SortVector[] searchJar(String jarBasePath) {
		//构建返回值
		SortVector[] reSvs = new SortVector[2];
		reSvs[0] = new SortVector<ZipFile>();
		reSvs[1] = new SortVector<IniConfProp>();
		//指定文件
		String subPath = "resources/.jpxres";
		//构建搜索到的文件路径序列
		List<String> jarPathList = new ArrayList<String>();
		try {
			//执行搜索
			SFilesUtil.getFileList(jarPathList,jarBasePath,null,"jar;zip",true,false);
			ZipFile     jarFile; //需要检查的jar文件对象
			ZipEntry    resEntry; //指定文件对象
			String      content;
			SDate       contentDate;
			long        mSec;
			IniConfProp icp; //配置文件信息解析类
			for(String ele:jarPathList) {
				jarFile = getZipFile(ele);
				if(jarFile==null) {
					continue;
				}
				resEntry = jarFile.getEntry(subPath);
				if(resEntry==null) {
					continue;
				}
				System.out.println("++++Begin Load Native Resource From jarFile:["+ele+"]");
				try {
					icp = new IniConfProp(jarFile.getInputStream(resEntry));
					content = icp.value("ver");
				}catch(Exception e) {
					continue;
				}
				if(content==null || content.length()<16) {
					continue;
				}
				if(content.length()==16) {
					content += ":00";
				}
				contentDate = new SDate(content);
				mSec = contentDate.getMillisecond();
				reSvs[0].add(jarFile,mSec);
				reSvs[1].add(icp,mSec);
			}
		}catch(Exception e) {
			e.printStackTrace();
		}
		//按照时间由老到新排列
		reSvs[0].asc();
		reSvs[1].asc(); 
		return reSvs;
	}
	
	/**
	 * 获取压缩文件对象
	 * @param path 文件全路径
	 * @return 压缩文件对象
	 * 2017年1月30日
	 * @author MBG
	 */
	private ZipFile getZipFile(String path) {
		try {
			return new ZipFile(new File(path));
		}catch(Exception e) {}
		return null;
	}
}
